package com.tsyz.servlet.util;

import com.tsyz.servlet.exception.BusinessException;
import com.tsyz.servlet.exception.ErrorEnumCode;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Optional;

/**
 * 金融计算专用的BigDecimal工具类
 * 提供精确的四则运算、比较和格式化功能
 *
 * @author ext.zhaoke14
 */
public class BigDecimalUtils {

    /**
     * 零值常量
     */
    public static final BigDecimal ZERO = BigDecimal.ZERO;
    /**
     * 一值常量
     */
    public static final BigDecimal ONE = BigDecimal.ONE;
    /**
     * 默认精度为2位小数，适用于大多数货币计算
     */
    private static final int DEFAULT_SCALE = 2;
    /**
     * 默认舍入模式为四舍五入
     */
    private static final RoundingMode DEFAULT_ROUNDING_MODE = RoundingMode.HALF_UP;

    /**
     * 加法运算
     *
     * @param one 被加数
     * @param tow 加数
     * @return 两数之和
     */
    public static BigDecimal add(BigDecimal one, BigDecimal tow) {
        if (one == null || tow == null) {
            throw new BusinessException(ErrorEnumCode.PARAM_NULL, "参数不能为null");
        }
        return one.add(tow);
    }

    /**
     * 减法运算
     *
     * @param minuend    被减数
     * @param subtrahend 减数
     * @return 两数之差
     */
    public static BigDecimal subtract(BigDecimal minuend, BigDecimal subtrahend) {
        if (minuend == null || subtrahend == null) {
            throw new BusinessException(ErrorEnumCode.PARAM_NULL, "参数不能为null");
        }
        return minuend.subtract(subtrahend);
    }

    /**
     * 乘法运算
     *
     * @param multiplicand 被乘数
     * @param multiplier   乘数
     * @return 两数之积
     */
    public static BigDecimal multiply(BigDecimal multiplicand, BigDecimal multiplier) {
        if (multiplicand == null || multiplier == null) {
            throw new BusinessException(ErrorEnumCode.PARAM_NULL, "参数不能为null");
        }
        return multiplicand.multiply(multiplier);
    }

    /**
     * 乘法运算（指定精度）
     *
     * @param multiplicand 被乘数
     * @param multiplier   乘数
     * @param scale        精度
     * @return 两数之积
     */
    public static BigDecimal multiply(BigDecimal multiplicand, BigDecimal multiplier, int scale) {
        return multiply(multiplicand, multiplier).setScale(scale, DEFAULT_ROUNDING_MODE);
    }

    /**
     * 除法运算
     *
     * @param dividend 被除数
     * @param divisor  除数
     * @return 两数之商
     */
    public static BigDecimal divide(BigDecimal dividend, BigDecimal divisor) {
        if (dividend == null || divisor == null) {
            throw new BusinessException(ErrorEnumCode.PARAM_NULL, "参数不能为null");
        }
        if (divisor.compareTo(BigDecimal.ZERO) == 0) {
            throw new BusinessException(ErrorEnumCode.BUSINESS_ERROR, "除数不能为零");
        }
        return dividend.divide(divisor, DEFAULT_SCALE, DEFAULT_ROUNDING_MODE);
    }

    /**
     * 除法运算（指定精度）
     *
     * @param dividend 被除数
     * @param divisor  除数
     * @param scale    精度
     * @return 两数之商
     */
    public static BigDecimal divide(BigDecimal dividend, BigDecimal divisor, int scale) {
        if (dividend == null || divisor == null) {
            throw new BusinessException(ErrorEnumCode.PARAM_NULL, "参数不能为null");
        }
        if (divisor.compareTo(BigDecimal.ZERO) == 0) {
            throw new BusinessException(ErrorEnumCode.BUSINESS_ERROR, "除数不能为零");
        }
        return dividend.divide(divisor, scale, DEFAULT_ROUNDING_MODE);
    }

    /**
     * 百分比计算
     *
     * @param amount     金额
     * @param percentage 百分比（如5表示5%）
     * @return 百分比结果
     */
    public static BigDecimal percentage(BigDecimal amount, BigDecimal percentage) {
        if (amount == null || percentage == null) {
            throw new BusinessException(ErrorEnumCode.PARAM_NULL, "参数不能为null");
        }
        return amount.multiply(percentage).divide(BigDecimal.valueOf(100), DEFAULT_SCALE, DEFAULT_ROUNDING_MODE);
    }

    /**
     * 比较两个BigDecimal值
     *
     * @param value1 值1
     * @param value2 值2
     * @return -1: value1 < value2, 0: value1 = value2, 1: value1 > value2
     */
    public static int compare(BigDecimal value1, BigDecimal value2) {
        if (value1 == null || value2 == null) {
            throw new BusinessException(ErrorEnumCode.PARAM_NULL, "参数不能为null");
        }
        return value1.compareTo(value2);
    }

    /**
     * 判断是否相等
     *
     * @param value1 值1
     * @param value2 值2
     * @return 是否相等
     */
    public static boolean equals(BigDecimal value1, BigDecimal value2) {
        if (value1 == null || value2 == null) {
            return false;
        }
        return value1.compareTo(value2) == 0;
    }

    /**
     * 判断是否大于
     *
     * @param value1 值1
     * @param value2 值2
     * @return value1是否大于value2
     */
    public static boolean greaterThan(BigDecimal value1, BigDecimal value2) {
        if (value1 == null || value2 == null) {
            throw new BusinessException(ErrorEnumCode.PARAM_NULL, "参数不能为null");
        }
        return value1.compareTo(value2) > 0;
    }

    /**
     * 判断是否大于等于
     *
     * @param value1 值1
     * @param value2 值2
     * @return value1是否大于等于value2
     */
    public static boolean greaterThanOrEqual(BigDecimal value1, BigDecimal value2) {
        if (value1 == null || value2 == null) {
            throw new BusinessException(ErrorEnumCode.PARAM_NULL, "参数不能为null");
        }
        return value1.compareTo(value2) >= 0;
    }

    /**
     * 判断是否小于
     *
     * @param value1 值1
     * @param value2 值2
     * @return value1是否小于value2
     */
    public static boolean lessThan(BigDecimal value1, BigDecimal value2) {
        if (value1 == null || value2 == null) {
            throw new BusinessException(ErrorEnumCode.PARAM_NULL, "参数不能为null");
        }
        return value1.compareTo(value2) < 0;
    }

    /**
     * 判断是否小于等于
     *
     * @param value1 值1
     * @param value2 值2
     * @return value1是否小于等于value2
     */
    public static boolean lessThanOrEqual(BigDecimal value1, BigDecimal value2) {
        if (value1 == null || value2 == null) {
            throw new BusinessException(ErrorEnumCode.PARAM_NULL, "参数不能为null");
        }
        return value1.compareTo(value2) <= 0;
    }

    /**
     * 取绝对值
     *
     * @param value 值
     * @return 绝对值
     */
    public static BigDecimal abs(BigDecimal value) {
        if (value == null) {
            throw new BusinessException(ErrorEnumCode.PARAM_NULL, "参数不能为null");
        }
        return value.abs();
    }

    /**
     * 四舍五入到指定小数位
     *
     * @param value 值
     * @param scale 小数位数
     * @return 四舍五入后的值
     */
    public static BigDecimal round(BigDecimal value, int scale) {
        if (value == null) {
            throw new BusinessException(ErrorEnumCode.PARAM_NULL, "参数不能为null");
        }
        return value.setScale(scale, RoundingMode.HALF_UP);
    }

    /**
     * 向下取整
     *
     * @param value 值
     * @param scale 小数位数
     * @return 向下取整后的值
     */
    public static BigDecimal floor(BigDecimal value, int scale) {
        if (value == null) {
            throw new BusinessException(ErrorEnumCode.PARAM_NULL, "参数不能为null");
        }
        return value.setScale(scale, RoundingMode.DOWN);
    }

    /**
     * 向上取整
     *
     * @param value 值
     * @param scale 小数位数
     * @return 向上取整后的值
     */
    public static BigDecimal ceil(BigDecimal value, int scale) {
        if (value == null) {
            throw new BusinessException(ErrorEnumCode.PARAM_NULL, "参数不能为null");
        }
        return value.setScale(scale, RoundingMode.UP);
    }

    /**
     * 将字符串转换为BigDecimal
     *
     * @param value 字符串值
     * @return BigDecimal值
     */
    public static BigDecimal toBigDecimal(String value) {
        if (value == null || value.trim().isEmpty()) {
            throw new BusinessException(ErrorEnumCode.PARAM_NULL, "参数不能为空");
        }
        try {
            return new BigDecimal(value);
        } catch (NumberFormatException e) {
            throw new BusinessException(ErrorEnumCode.BUSINESS_ERROR, "无效的数字格式: " + value);
        }
    }

    /**
     * 将double转换为BigDecimal（注意：可能存在精度问题）
     *
     * @param value double值
     * @return BigDecimal值
     */
    public static BigDecimal toBigDecimal(double value) {
        return BigDecimal.valueOf(value);
    }

    /**
     * 格式化金额显示（添加千分位分隔符）
     *
     * @param amount 金额
     * @return 格式化后的字符串
     */
    public static String formatAmount(BigDecimal amount) {
        if (amount == null) {
            throw new BusinessException(ErrorEnumCode.PARAM_NULL, "参数不能为null");
        }
        return amount.setScale(DEFAULT_SCALE, DEFAULT_ROUNDING_MODE).toString().replaceAll("\\B(?=(\\d{3})+(?!\\d))", ",");
    }

    /**
     * 判断是否为负数
     *
     * @param value 值
     * @return 是否为负数
     */
    public static boolean isNegative(BigDecimal value) {
        if (value == null) {
            return false;
        }
        return value.compareTo(BigDecimal.ZERO) < 0;
    }

    /**
     * 判断是否为正数
     *
     * @param value 值
     * @return 是否为正数
     */
    public static boolean isPositive(BigDecimal value) {
        if (value == null) {
            return false;
        }
        return value.compareTo(BigDecimal.ZERO) > 0;
    }

    /**
     * 判断是否为零
     *
     * @param value 值
     * @return 是否为零
     */
    public static boolean isZero(BigDecimal value) {
        if (value == null) {
            return false;
        }
        return value.compareTo(BigDecimal.ZERO) == 0;
    }

    /**
     * 幂运算
     *
     * @param base     底数
     * @param exponent 指数
     * @return 幂运算结果
     */
    public static BigDecimal pow(BigDecimal base, int exponent) {
        if (base == null) {
            throw new BusinessException(ErrorEnumCode.PARAM_NULL, "底数不能为null");
        }
        return base.pow(exponent);
    }

    // ======== 新增功能扩展 ========

    /**
     * 获取多个值中的最大值
     *
     * @param values 值数组
     * @return 最大值
     */
    public static BigDecimal max(BigDecimal... values) {
        if (values == null || values.length == 0) {
            throw new BusinessException(ErrorEnumCode.PARAM_NULL, "参数不能为空");
        }

        BigDecimal max = values[0];
        for (int i = 1; i < values.length; i++) {
            if (values[i] != null && (max == null || values[i].compareTo(max) > 0)) {
                max = values[i];
            }
        }

        if (max == null) {
            throw new BusinessException(ErrorEnumCode.PARAM_NULL, "所有参数都不能为null");
        }

        return max;
    }

    /**
     * 获取多个值中的最小值
     *
     * @param values 值数组
     * @return 最小值
     */
    public static BigDecimal min(BigDecimal... values) {
        if (values == null || values.length == 0) {
            throw new BusinessException(ErrorEnumCode.PARAM_NULL, "参数不能为空");
        }

        BigDecimal min = values[0];
        for (int i = 1; i < values.length; i++) {
            if (values[i] != null && (min == null || values[i].compareTo(min) < 0)) {
                min = values[i];
            }
        }

        if (min == null) {
            throw new BusinessException(ErrorEnumCode.PARAM_NULL, "所有参数都不能为null");
        }

        return min;
    }

    /**
     * 累加运算
     *
     * @param values 值数组
     * @return 累加结果
     */
    public static BigDecimal sum(BigDecimal... values) {
        if (values == null || values.length == 0) {
            return BigDecimal.ZERO;
        }

        BigDecimal result = BigDecimal.ZERO;
        for (BigDecimal value : values) {
            if (value != null) {
                result = result.add(value);
            }
        }

        return result;
    }

    /**
     * 累乘运算
     *
     * @param values 值数组
     * @return 累乘结果
     */
    public static BigDecimal product(BigDecimal... values) {
        if (values == null || values.length == 0) {
            return BigDecimal.ONE;
        }

        BigDecimal result = BigDecimal.ONE;
        for (BigDecimal value : values) {
            if (value != null) {
                result = result.multiply(value);
            }
        }

        return result;
    }

    /**
     * 安全除法运算，返回Optional避免异常
     *
     * @param dividend 被除数
     * @param divisor  除数
     * @return 计算结果的Optional包装
     */
    public static Optional<BigDecimal> safeDivide(BigDecimal dividend, BigDecimal divisor) {
        try {
            return Optional.of(divide(dividend, divisor));
        } catch (BusinessException e) {
            return Optional.empty();
        }
    }

    /**
     * 带溢出检查的加法运算
     *
     * @param augend 被加数
     * @param addend 加数
     * @return 两数之和
     * @throws BusinessException 当结果溢出时抛出
     */
    public static BigDecimal addWithOverflowCheck(BigDecimal augend, BigDecimal addend) {
        if (augend == null || addend == null) {
            throw new BusinessException(ErrorEnumCode.PARAM_NULL, "参数不能为null");
        }

        try {
            return augend.add(addend);
        } catch (ArithmeticException e) {
            throw new BusinessException(ErrorEnumCode.BUSINESS_ERROR, "加法运算溢出");
        }
    }

    /**
     * 格式化金额显示（可自定义小数位数）
     *
     * @param amount 金额
     * @param scale  小数位数
     * @return 格式化后的字符串
     */
    public static String formatAmount(BigDecimal amount, int scale) {
        if (amount == null) {
            throw new BusinessException(ErrorEnumCode.PARAM_NULL, "参数不能为null");
        }
        return amount.setScale(scale, DEFAULT_ROUNDING_MODE).toString().replaceAll("\\B(?=(\\d{3})+(?!\\d))", ",");
    }

    /**
     * 格式化为百分比显示
     *
     * @param value 值
     * @return 百分比字符串
     */
    public static String formatPercentage(BigDecimal value) {
        if (value == null) {
            throw new BusinessException(ErrorEnumCode.PARAM_NULL, "参数不能为null");
        }
        return value.multiply(new BigDecimal("100")).setScale(2, DEFAULT_ROUNDING_MODE).toString() + "%";
    }

    /**
     * 验证金额是否有效（非负且精度合理）
     *
     * @param amount 金额
     * @return 是否有效
     */
    public static boolean isValidAmount(BigDecimal amount) {
        return amount != null && amount.compareTo(BigDecimal.ZERO) >= 0 && amount.scale() <= DEFAULT_SCALE;
    }

    /**
     * 验证百分比是否有效（0-100之间）
     *
     * @param percentage 百分比
     * @return 是否有效
     */
    public static boolean isValidPercentage(BigDecimal percentage) {
        return percentage != null && percentage.compareTo(BigDecimal.ZERO) >= 0 && percentage.compareTo(new BigDecimal("100")) <= 0;
    }

    /**
     * 货币单位转换（分转元）
     *
     * @param cent 分
     * @return 元
     */
    public static BigDecimal centToYuan(Long cent) {
        if (cent == null) {
            throw new BusinessException(ErrorEnumCode.PARAM_NULL, "参数不能为null");
        }
        return new BigDecimal(cent).divide(new BigDecimal("100"), DEFAULT_SCALE, DEFAULT_ROUNDING_MODE);
    }

    /**
     * 货币单位转换（元转分）
     *
     * @param yuan 元
     * @return 分
     */
    public static Long yuanToCent(BigDecimal yuan) {
        if (yuan == null) {
            throw new BusinessException(ErrorEnumCode.PARAM_NULL, "参数不能为null");
        }
        return yuan.multiply(new BigDecimal("100")).longValue();
    }
}
